import { ref, reactive, onUnmounted, getCurrentInstance, nextTick } from 'vue';

/**
 * 任务执行结果结构
 * @property taskId - 任务唯一标识符（必需）
 * @property task - 任务指针
 * @property success - 标记任务是否成功完成
 * @property data - 任务返回的业务数据（可选）
 * @property error - 失败时的错误对象（可选）
 * @property errorType - 错误类型（可选）
 */
export interface Result{
	taskId: string, 
	task: Task, 
	success: boolean, 
	data?: unknown, 
	error?: Error, 
	errorType?: TaskErrorType, 
}

/**
 * 任务执行上下文
 * @property results - 历史任务结果集合（数组结构）
 * @property data - 上下文共享数据（可选）
 * @property chainName - 所属的任务链名称（可选）
 * @property chainId - 所属的任务链唯一标识符（可选）
 * @property errorType - 错误类型（可选）
 * @property errorMsg - 错误信息（可选）
 * @property updateTime - 更新时间
 */
export interface Context{
	results: Result[], 
	data?: { [key : string] : any }, 
	chainName?: string, 
	chainId?: string, 
	errorType?: TaskErrorType, 
	errorMsg?: string, 
	updateTime: number, 
}

/**
 * 任务配置选项
 * @property action - 任务执行函数，接收上下文和可选数据，返回数据或者结果，返回false时执行失败
 * @property addNow - 是否立即加入队列（默认false）
 * @property type - 任务类型（可选，默认normal）
 * @property id - 手动指定任务ID（可选）
 * @property data - 任务私有数据（可选）
 * @property chainName - 所属任务链名称（可选）
 * @property beforeTask - 依赖的前置任务id数组（可选）
 */
export interface TaskOptions{
	action: (context: Context, data?: unknown) => unknown, 
	addNow: boolean, 
	type?: TaskType, 
	id?: string, 
	data?: unknown, 
	chainName?: string, 
	beforeTask?: string[], 
	triggerMode?: TaskTriggerMode, 
}

/**
 * 队列状态枚举
 * @enum {string}
 * @readonly 
 */
export enum QueueStatus{
	SLEEPING = 'SLEEPING', // 休眠状态
	WAITING = 'WAITING', // 等待任务状态
	RUNNING = 'RUNNING', // 正在执行状态
	CLOSING = 'CLOSING', // 关闭中状态
	CLOSED = 'CLOSED', // 已关闭状态
	ERROR_CLOSED = 'ERROR_CLOSED', // 异常关闭状态
}

/**
 * 任务状态枚举
 * @enum {string}
 * @readonly 
 */
export enum TaskStatus{
	ALONE = 'ALONE', // 独立任务状态
	WAITING = 'WAITING', // 在队列中等待执行
	RUNNING = 'RUNNING', // 正在执行中
	COMPLETED = 'COMPLETED', // 执行完成
	FAILED = 'FAILED', // 执行失败
}

/**
 * 任务类型枚举
 * @enum {string}
 * @readonly 
 */
export enum TaskType{
	NORMAL = 'NORMAL', // 常规任务类型
	REQUEST = 'REQUEST', // 请求型任务（默认互斥）
	//LOCK_REQUEST = 'LOCK_REQUEST', // 互斥的请求型任务
}

/**
 * 任务错误类型枚举
 * @enum {string}
 * @readonly 
 */
export enum TaskErrorType{
	UNKNOWN = 'UNKNOWN',  // 未知错误
	RUN_TIMEOUT = 'RUN_TIMEOUT',  // 运行超时
	WAIT_TIMEOUT = 'WAIT_TIMEOUT', // 等待超时
	RUN_ERROR = 'RUN_ERROR', // 运行时错误
	REQUEST_ERROR = 'REQUEST_ERROR', // 请求错误
	CHAIN_STOP = 'CHAIN_STOP', // 任务链中其他任务失败导致的链条中止
	RETURN_FALSE = 'RETURN_FALSE', // 任务执行后返回false
}

/**
 * 时间单位枚举
 * @enum {number}
 * @readonly 
 */
export enum TimeUnits{
	MilliSecond = 1, // 毫秒
	Second = 1000, // 秒
	Minute = 60 * 1000, // 分钟
	Hour = 60 * 60 * 1000, // 小时
	Day = 24 * 60 * 60 * 1000, // 天
}

/**
 * 任务触发模式枚举
 * @enum {string}
 * @readonly 
 */
export enum TaskTriggerMode{
	And = 'And', // 所有前置任务成功才能触发
	Or = 'Or', // 所有前置任务有一个成功就能触发
}

interface EventListeners {
    start: Function[],
    complete: Function[],
    fail: Function[],
    finally: Function[],
}

interface StatsType {
    total: number,
    completed: number,
    completedIds: string[],
    failed: number,
    failedIds: string[],
    running: number,
}

function startTaskLog(context: Context, task: Task): void {
	console.log('chain: ' + task.chainId);
	console.log('task: ' + task.id + ' start!');
}

function endTaskLog(context: Context, task: Task): void {
	console.log('chain: ' + task.chainId);
	console.log('task: ' + task.id + ' end!');
	console.log('result: ' + JSON.stringify(task.result, (key, value) => 
		key === 'task' ? undefined : value
	));
	if (task.result && !task.result.success) {
		console.log(task.result.error);
	}
}

export class Task {
	private static readonly BASE_CONFIGS = {
		MAX_RUNNING_NUM: 10, 
		MAX_TASK_QUEUE_NUM: 2000, 
		MAX_WAITING_TIME: 10 * TimeUnits.Minute, 
		MAX_RUNNING_TIME: 2 * TimeUnits.Minute, 
		MAX_TASK_TIME: 6 * TimeUnits.Hour, 
		MAX_TASK_ALONE_TIME: 2 * TimeUnits.Minute, 
		// MAX_CONTEXT_TIME: 6 * 60 * 60 * 1000, 
		// MAX_ARR_LENGTH: 10000, 
		MAX_MAP_SIZE: 10000, 
		LARGE_DELETE_NUM: 1000, 
		RUN_OBSERVATION_INTERVAL: 20, 
		WAIT_OBSERVATION_INTERVAL: 30 * TimeUnits.Second, 
		SLEEP_OBSERVATION_INTERVAL: 10 * TimeUnits.Minute, 
		TIME_TO_WAIT: 10 * TimeUnits.Second, 
		TIME_TO_SLEEP: 5 * TimeUnits.Minute, 
	} as const
	
	private static _TaskQueue: Task[] = []; 
	private static _ObserverTimer: number | null; 
	private static _RunningTasks: Map<string, Task> = new Map();
	private static _AllTasks: Map<string, Task> = new Map();
	private static _ChainData: Map<string, unknown> = new Map();
	private static _ChainContext: Map<string, Context> = new Map();
	private static _Listeners: EventListeners = {
		start: [startTaskLog],
		complete: [],
		fail: [],
		finally: [endTaskLog],
	};
	private static _Stats = reactive<StatsType>({
		total: 0, 
		completed: 0, 
		completedIds: [], 
		failed: 0, 
		failedIds: [], 
		running: 0, 
	});
	private static _Status: QueueStatus | null = null; 
	private static _StatusTime: number; 
	
	
	private _id: string; 
	private _listeners: EventListeners = {
		start: [], 
		complete: [], 
		fail: [], 
		finally: [], 
	}; 
	private _type: TaskType; 
	private _status: TaskStatus = TaskStatus.ALONE; 
	private _times = {
		buildTime: -1, 
		entryTime: -1, 
		startTime: -1, 
		endTime: -1, 
	}
	private _action: (context: Context, data?: unknown) => unknown; 
	private _data?: unknown;
	private _canStart: (context: Context, data?: unknown) => boolean =
		(_context) => true; 
	private _chainTasks: {
		beforeTask: string[],
		afterTask: string[], 
	} = {
		beforeTask: [], 
		afterTask: [], 
	} 
	private _triggerMode: TaskTriggerMode = TaskTriggerMode.And;
	private _chainName?: string; 
	private _chainId?: string; 
	private _result?: Result; 
	
	
	static {
		Task.startRunning();
	}
	
	
	constructor(opt: TaskOptions) {
		// debugger;
		console.log(opt);
		this._type = opt.type??TaskType.NORMAL;
		if (Task.getTask(opt.id)) {
			throw new Error(`the id of task:${opt.id} is already exist!`)
		}
		this._id = opt.id??Task.getNewId(this._type);
		this._action = opt.action;
		this.data = opt.data;
		this._triggerMode = opt.triggerMode??TaskTriggerMode.And;
		this.chainName = opt.chainName;
		this._chainTasksInitHandler(opt.beforeTask || []);
		this._times.buildTime = Date.now();
		Task._AllTasks.set(this.id, this);
		if (opt.addNow) {
			this.addToQueue();
		}
	}
	
	
	// private static get taskQueue() { return Task._TaskQueue };
	private static async _observerAsync() {
		try {
			switch (Task._Status) {
				case QueueStatus.SLEEPING: 
				case QueueStatus.WAITING: 
				Task.checkOldTaskExpired();
				case QueueStatus.RUNNING: 
				{
					if (Task._RunningTasks.size > 0) {
						Task._checkFinish();
					}
					if (Task._TaskQueue.length > 0) {
						Task._checkActive();
					} else if (Task._RunningTasks.size === 0) {
						Task._checkStatusTime();
					}
				}
				break;
				case QueueStatus.CLOSING:
				{
					if (Task._RunningTasks.size > 0) {
						Task._checkFinish();
					}
					if (Task._RunningTasks.size === 0) {
						Task._changeStatus(QueueStatus.CLOSED);
						Task._clean();
						return;
					}
				}
				break;
				case QueueStatus.CLOSED: 
				return;
				case QueueStatus.ERROR_CLOSED:
				{
					Task._clean();
				}
				return;
			}
			switch (Task._Status) {
				case QueueStatus.SLEEPING:
				Task._ObserverTimer = setTimeout(
					() => Task._observerAsync(), 
					Task.BASE_CONFIGS.SLEEP_OBSERVATION_INTERVAL
				)
				break;
				case QueueStatus.WAITING:
				Task._ObserverTimer = setTimeout(
					() => Task._observerAsync(), 
					Task.BASE_CONFIGS.WAIT_OBSERVATION_INTERVAL
				)
				break;
				case QueueStatus.RUNNING:
				case QueueStatus.CLOSING:
				Task._ObserverTimer = setTimeout(
					() => Task._observerAsync(), 
					Task.BASE_CONFIGS.RUN_OBSERVATION_INTERVAL
				)
				break;
			}
		} catch (err: unknown) {
			if (err instanceof Error) {
				console.log("错误：" + err.message);
			} else {
				console.log('未知错误：' + String(err));
			}
			Task._changeStatus(QueueStatus.ERROR_CLOSED);
		}
	}
	private static _clean() {
		Task._TaskQueue = [];
		Task._RunningTasks.clear();
		Task._AllTasks.clear();
		Task._ChainData.clear();
		Task._ChainContext.clear();
		Task._cleanTimer();
	}
	private static _cleanTimer() {
		if (Task._ObserverTimer !== null) {
			clearTimeout(Task._ObserverTimer);
			Task._ObserverTimer = null;
		}
	}
	private static _checkFinish() {
		const runningTasks = [...Task._RunningTasks.values()];
		for (let task of runningTasks) { 
			const runningTime = Date.now() - task._times.startTime;
			if (task.result) {
				// Task.snapshot();
				// console.log(task.id);
				task._taskResultHandler(task.result);
				Task._RunningTasks.delete(task.id);
				Task._Stats.running = Task._RunningTasks.size;
				if (task.result.success) {
					for (let nextTaskId of task._chainTasks.afterTask) {
						const nextTask = Task.getTask(nextTaskId);
						if (nextTask) {
							const index = Task._TaskQueue.indexOf(nextTask);
							if (index >= 0) {
								Task._TaskQueue.splice(index, 1);
								Task._checkAndActive(nextTask);
							}
						}
					}
				}
			} else if (
				runningTime > Task.BASE_CONFIGS.MAX_RUNNING_TIME
			) {
				const result = {
					taskId: task.id,
					task,
					success: false, 
					errorType: TaskErrorType.RUN_TIMEOUT
				} as Result
				task._taskResultHandler(result);
				Task._RunningTasks.delete(task.id);
				Task._Stats.running = Task._RunningTasks.size;
			}
		}
	}
	private static _checkActive() {
		for (let i = 0; i < Task._TaskQueue.length; i++) {
			if (Task._RunningTasks.size >= 
				Task.BASE_CONFIGS.MAX_RUNNING_NUM) 
			{ return; }
			Task._checkAndActive(Task._TaskQueue.shift() as Task);
		}
	}
	private static _checkStatusTime() {
		const now = Date.now();
		switch (Task._Status) {
			case QueueStatus.WAITING:
			if (
				now - Task._StatusTime >= 
					Task.BASE_CONFIGS.TIME_TO_SLEEP
			) { Task._changeStatus(QueueStatus.SLEEPING) }
			break;
			case QueueStatus.RUNNING:
			if (
				now - Task._StatusTime >= 
					Task.BASE_CONFIGS.TIME_TO_WAIT
			) { Task._changeStatus(QueueStatus.WAITING) }
			break;
		}
	}
	private static _knock() {
		console.log('_knock Status: ' + Task._Status);
		Task._Status = null;
		Task._cleanTimer();
		Task.startRunning();
	}
	private static _changeStatus(newStatus: QueueStatus) {
		console.log('task queue change status to ' + newStatus);
		Task._Status = newStatus;
		Task._StatusTime = Date.now();
	}
	private static _checkAndActive(task: Task) {
		if (Task._checkBeforeStart(task)) {
			Task._active(task);
			Task._Stats.running = Task._RunningTasks.size;
		}
	}
	private static _checkBeforeStart(task: Task): boolean {
		// console.log(task);
		// Task.snapshot();
		// 检查等待是否超时
		const waitingTime = Date.now() - task._times.entryTime
		if (waitingTime > Task.BASE_CONFIGS.MAX_WAITING_TIME) {
			const result = {
				taskId: task.id,
				task,
				success: false,
				errorType: TaskErrorType.WAIT_TIMEOUT
			} as Result;
			task._taskResultHandler(result);
			return false;
		}
		// 检查前置任务
		let tasksBeforeCheck = Task._checkBeforeTaskArr(task);
		if (tasksBeforeCheck == null) {
			const result = {
				taskId: task.id, 
				task,
				success: false,
				errorType: TaskErrorType.CHAIN_STOP
			} as Result;
			task._taskResultHandler(result);
			return false;
		}
		// 检查是否有互斥的任务正在运行
		let requestRunning = false;
		if (task._type === TaskType.REQUEST) {
			requestRunning = [...Task._RunningTasks.values()].some(
				runTask => runTask._type === TaskType.REQUEST
			);
		}
		// 运行任务自定义的开始检测
		let taskSelfCheck = false;
		try {
			taskSelfCheck = task.startCheck();
		} catch (err: unknown) {
			const result = {
				taskId: task.id, 
				task,
				success: false,
				error: undefined,
				errorType: undefined,
			} as Result;
			Task.getTaskError(err, (errErr, type) => {
				result.error = errErr;
				result.errorType = type;
			})
			task._taskResultHandler(result);
			return false;
		}
		if (!tasksBeforeCheck || !taskSelfCheck || requestRunning) {
			Task._TaskQueue.push(task);
			return false;
		}
		return true;
	}
	private static _checkBeforeTaskArr(task: Task): boolean | null {
		if (task._chainTasks.beforeTask.length === 0) {
			return true;
		}
		let tasksBeforeCheck: boolean | null = false;
		switch (task._triggerMode) {
			case TaskTriggerMode.And:
			const oneFailed = task._chainTasks.beforeTask.some(id => 
				Task._Stats.failedIds.includes(id)
			)
			if (oneFailed) {
				tasksBeforeCheck = null;
				break;
			}
			tasksBeforeCheck = task._chainTasks.beforeTask.every(id => 
				Task._Stats.completedIds.includes(id)
			)
			break;
			case TaskTriggerMode.Or:
			const allFailed = task._chainTasks.beforeTask.every(id => 
				Task._Stats.failedIds.includes(id)
			)
			if (allFailed) {
				tasksBeforeCheck = null;
				break;
			}
			tasksBeforeCheck = task._chainTasks.beforeTask.some(id => 
				Task._Stats.completedIds.includes(id)
			)
			break;
		}
		return tasksBeforeCheck;
	}
	private static _active(task: Task) {
		task._times.startTime = Date.now();
		const context = task.getChainContext();
		const data = task._data;
		const totalAction = async function(task: Task) : Promise<unknown> {
			const timeoutPromise = new Promise((_, reject) => {
				setTimeout(() => reject(new Error(TaskErrorType.RUN_TIMEOUT)), 
					Task.BASE_CONFIGS.MAX_RUNNING_TIME);
			})
			const taskPromise = (async() => {
				Task._Listeners.start.forEach(func => func(context, task));
				task._listeners.start.forEach(func => func(context, data));
				return task._action(context as Context, data);
			})()
			
			return Promise.race([timeoutPromise, taskPromise]);
		}
		// debugger;
		totalAction(task).then((data) => {
			// debugger;
			let result = null;
			if (data === false) {
				console.error();
				result = {
					taskId: task.id, 
					task, 
					success: false, 
					errorType: TaskErrorType.UNKNOWN 
				} as Result; 
				task._result = result; 
			} else if (Task.isResult(data)) {
				task._result = data; 
			} else {
				result = {
					taskId: task.id,
					task, 
					success: true, 
					data,
				} as Result;
				task._result = result;
			}
		})
		.catch((err: unknown) => {
			const result = {
				taskId: task.id,
				task, 
				success: false, 
				error: undefined, 
				errorType: undefined, 
			} as Result
			Task.getTaskError(err, (errErr, type) => {
				result.error = errErr;
				result.errorType = type;
			})
			task._result = result;
		})
		Task._RunningTasks.set(task.id, task);
	}
	private static _getFirstTimeFromArr = (arr: string[]): number => {
		while (true) {
			if (arr.length > 0) {
				const taskId = arr[0];
				const time = Task.getTask(taskId)?._times.endTime;
				if (!time) {
					arr.shift();
				} else {
					return time;
				}
			} else {
				return -1;
			}
		}
	}
	private static _deleteOldTask(num: number) {
		for (let i = 1; i < num; i++) {
			const getTime = (arr: string[]): number => {
				return Task._getFirstTimeFromArr(arr);
			}
			const cTaskTime = getTime(Task._Stats.completedIds);
			const fTaskTime = getTime(Task._Stats.failedIds);
			const taskId = cTaskTime < 0 ? 
				fTaskTime < 0 ? null : 
				Task._Stats.failedIds[0] : 
				fTaskTime < 0 ? Task._Stats.completedIds[0] : 
				cTaskTime < fTaskTime ? 
				Task._Stats.completedIds[0] : 
				Task._Stats.failedIds[0];
			if (!taskId) {
				return;
			}
			const task = Task.getTask(taskId);
			if (task) {
				Task._ChainContext.delete(task._chainId as string);
			}
			Task._AllTasks.delete(taskId as string);
		}
	}
	private static snapshot() {
		console.log(Task._TaskQueue.map(task => task.id));
		console.log([...Task._RunningTasks.values()]
			.map(task => task.id));
		console.log([...Task._AllTasks.values()]
			.map(task => task.id));
		console.log(Task._Stats);
		
	}
	
	public static newContext(overrides: Partial<Context> = {}): Context {
		return {
			results: [], 
			...overrides, 
			updateTime: Date.now(),
		};
	}
	public static newResult(task: Task, success: boolean): Result {
		return {
			taskId: task.id, 
			task, 
			success, 
		}
	}
	public static get status() { return Task._Status }
	public static reStart() {
		Task.closeNow();
		Task.startRunning();
	}
	public static startRunning() {
		console.log("startRunning Status: " + Task._Status);
		switch (Task._Status) {
			case QueueStatus.SLEEPING:
			case QueueStatus.WAITING:
			case QueueStatus.RUNNING:
			case QueueStatus.CLOSING:
			case QueueStatus.ERROR_CLOSED:
			return;
		}
		Task._Status = QueueStatus.RUNNING;
		Task._StatusTime = Date.now();
		Task._ObserverTimer = setTimeout(
			() => Task._observerAsync(), 
			Task.BASE_CONFIGS.RUN_OBSERVATION_INTERVAL
		);
	}
	public static close() {
		Task._changeStatus(QueueStatus.CLOSING);
		Task._TaskQueue = [];
	}
	public static closeNow() {
		Task._changeStatus(QueueStatus.CLOSED);
		Task._clean();
	}
	public static getTaskError(err: unknown, 
		callBack: (error: Error, errorTypr: TaskErrorType) => void
	) {
		// debugger;
		console.log(err);
		let error = null;
		let errorType = null;
		if (err instanceof Error) {
			error = err;
			if (err.message == TaskErrorType.RUN_TIMEOUT) {
				errorType = TaskErrorType.REQUEST_ERROR;
			} else if (err instanceof TypeError || err instanceof RangeError) {
				errorType = TaskErrorType.RUN_ERROR;
			} else {
				errorType = TaskErrorType.UNKNOWN;
			}
		} else {
			error = new Error(String(err));
			errorType = TaskErrorType.UNKNOWN;
		}
		callBack(error, errorType);
	}
	public static getNewId(prefix: string, length: number = 8): string {
		const suffix = Date.now().toString(36).slice(-4);
		return `${prefix}-${Task.generateRandomId(length)}-${suffix}`;
	}
	public static generateRandomId(length: number = 8): string {
		const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
		let result = '';
		for (let i = 0; i < length; i++) {
			result += chars.charAt(Math.floor(Math.random() * chars.length));
		}
		return result;
	}
	/**
	 * 在时间间隔内生成一样的id
	 */
	public static getNewIdByTime(prefix: string, interval: number, unit: TimeUnits) {
		const time = interval * unit;
		const now = Date.now();
		const suffix = Math.round(now / time) * time;
		return `${prefix}-${suffix}`;
	}
	public static isResult(data: unknown): data is Result {
		const obj = data as Result;
		return (
			!!obj &&
			typeof obj.taskId === 'string' &&
			typeof obj.success === 'boolean' &&
			(obj.error === undefined || obj.error instanceof Error)
		);
	} 
	// public static isNull(data: unknown): boolean {
	// 	return data == null || data == undefined;
	// }
	public static getTask(taskId: string | undefined | null) {
		if (!taskId) {
			return undefined;
		}
		return Task._AllTasks.get(taskId as string);
	}
	public static checkOldTaskExpired() {
		Task.snapshot();
		//处理长时间没进队列的task
		const allTasks = [...Task._AllTasks.values()];
		for (let task of allTasks) {
			const aloneTime = Date.now() - task._times.buildTime;
			if (task._times.entryTime === -1 && 
				aloneTime > Task.BASE_CONFIGS.MAX_TASK_ALONE_TIME
			) {
				Task._AllTasks.delete(task.id);
			}
		}
		//从已完成和出错的列表里面寻找过期task
		const getTime = (arr: string[]): number => {
			return Task._getFirstTimeFromArr(arr);
		}
		const cTaskTime = getTime(Task._Stats.completedIds);
		const fTaskTime = getTime(Task._Stats.failedIds);
		const validTimes = [cTaskTime, fTaskTime].filter(time => time > 0);
		const passedTime = validTimes.length > 0 ? 
			Date.now() - Math.min(...validTimes) : 0;
		if (
			passedTime > Task.BASE_CONFIGS.MAX_TASK_TIME
		) {
			Task._deleteOldTask(1);
			Task.checkOldTaskExpired();
		}
		//如果列表太长，触发删除旧task
		const size = Task._AllTasks.size;
		if (
			size > Task.BASE_CONFIGS.MAX_MAP_SIZE
		) {
			let deleteNum = Math.max(
				size - Task.BASE_CONFIGS.MAX_MAP_SIZE, 
				Task.BASE_CONFIGS.LARGE_DELETE_NUM
			);
			Task._deleteOldTask(deleteNum);
		}
	}
	public static setChainData(chainName: string, data: unknown) {
		if (data !== undefined) {
			Task._ChainData.set(chainName, data);
		}
	}
	public static getChainData(chainName?: string) {
		if (!chainName) {
			return undefined;
		}
		return Task._ChainData.get(chainName as string);
	}
	public static async waitTaskFinish(taskId: string) {
		let task = Task.getTask(taskId);
		if (task == undefined) {
			throw new Error('no task find by this id');
		}
		task = task as Task;
		while (!task._result || Task._RunningTasks.get(task.id)) {
			await new Promise(resolve => setTimeout(resolve, 
				Task.BASE_CONFIGS.RUN_OBSERVATION_INTERVAL));
			task = Task.getTask(taskId);
			if (task == undefined) {
				throw new Error('task has been removed');
			}
			task = task as Task;
		}
		return task._result;
	}
	public static getResultData(context: Context, taskId?: string) {
		if (!context) {
			console.log('no context');
			return undefined;
		}
		const results = context.results;
		if (!taskId) {
			if (results.length > 0) {
				return results[results.length - 1].data;
			} else {
				return undefined;
			}
		} else {
			for (let result of results) {
				if (result.taskId === taskId) {
					return result.data;
				}
			}
			return undefined;
		}
	}
	
	
	public getChainContext(): Context | undefined {
		if (!this._chainId) {
			return undefined;
		}
		return Task._ChainContext.get(this._chainId as string);
	}
	public get id() { return this._id; }
	public onStart(func: (context: Context, data?: unknown) => void) {
		this._checkFunc(func);
		this._checkNotInQueue();
		this._listeners.start.push(func);
	}
	public onComplete(func: (context: Context, data?: unknown) => void) {
		this._checkFunc(func);
		this._checkNotInQueue();
		this._listeners.complete.push(func);
	}
	public onFail(func: (context: Context, data?: unknown) => void) {
		this._checkFunc(func);
		this._checkNotInQueue();
		this._listeners.fail.push(func);
	}
	public onFinally(func: (context: Context, data?: unknown) => void) {
		this._checkFunc(func);
		this._checkNotInQueue();
		this._listeners.finally.push(func);
	}
	public set action(newAction: (context: Context, data?: unknown) => unknown) { 
		this._checkFunc(newAction);
		this._checkNotInQueue();
		this._action = newAction;
	};
	// public get action() { return this._action; }
	public set data(data: unknown) {
		this._checkNotInQueue();
		this._data = data;
	};
	public get data() { return this._data; }
	public set canStart(newFunc: (context: Context, data?: unknown) => boolean) {
		this._checkFunc(newFunc);
		this._checkNotInQueue();
		this._canStart = newFunc;
	};
	public get canStart() { return this._canStart; }
	public beforeTask(...ids: string[]) : void;
	public beforeTask() : string[];
	public beforeTask(...ids: any[]) : any {
		if (ids.length > 0) {
			this._chainTasks.beforeTask.concat(ids);
		} else {
			return this._chainTasks.beforeTask;
		}
	}
	public afterTask(...ids: string[]) : void;
	public afterTask() : string[];
	public afterTask(...ids: any[]) : any {
		if (ids.length > 0) {
			this._chainTasks.afterTask.concat(ids);
		} else {
			return this._chainTasks.afterTask;
		}
	}
	public set triggerMode(mode: TaskTriggerMode) {
		this._triggerMode = mode;
	}
	public get triggerMode() {
		return this._triggerMode;
	}
	public set chainName(name: string | undefined) { 
		this._checkNotInQueue(); 
		this._chainName = name; 
	};
	public get chainName() { return this._chainName };
	public get chainId() { return this._chainId };
	public get result() { return this._result };
	
	
	// public async exec(context: Context, data?: unknown) {
	// 	return this._action(context, data);
	// };
	public startCheck() : boolean {
		return this._canStart(this.getChainContext() as Context, this._data);
	};
	public addToQueue() {
		if (Task._TaskQueue.length > Task.BASE_CONFIGS.MAX_TASK_QUEUE_NUM) {
			throw new Error('Task queue is too long!');
		}
		this._times.entryTime = Date.now();
		switch (Task._Status) {
			case QueueStatus.WAITING:
			case QueueStatus.SLEEPING:
			Task._knock();
			break;
			case QueueStatus.CLOSING:
			case QueueStatus.CLOSED:
			case QueueStatus.ERROR_CLOSED:
			throw new Error('Queue has closed');
		}
		Task._TaskQueue.push(this);
		Task._Stats.total++;
	};
	private _checkFunc(func: (...args: any[]) => any) {
		if (typeof func !== 'function') {
			throw new Error('Parameter must be a function!');
		}
	}
	private _checkNotInQueue() {
		if (this._status !== TaskStatus.ALONE) {
			throw new Error('Task is already in queue!');
		}
	}
	private _taskResultHandler(result: Result) {
		this._result = result;
		this._times.endTime = Date.now();
		let context = this.getChainContext();
		if (context) {
			context = context as Context;
			context.results.push(result);
			context.errorType = context.errorType??result.errorType;
			context.errorMsg = context.errorMsg??result.error?.message;
			context.updateTime = Date.now();
		}
		if (result.success) {
			nextTick(() => {
				Task._Stats.completed++;
				Task._Stats.completedIds.push(this.id);
			});
		} else {
			nextTick(() => {
				Task._Stats.failed++;
				Task._Stats.failedIds.push(this.id);
			});
		}
		const data = this._data;
		try {
			if (result.success) {
				this._listeners.complete.forEach(
					func => func(context, data)
				);
				Task._Listeners.complete.forEach(
					func => func(context, this)
				);
			} else {
				this._listeners.fail.forEach(
					func => func(context, data)
				);
				Task._Listeners.fail.forEach(
					func => func(context, this)
				);
			}
			this._listeners.finally.forEach(
				func => func(context, data)
			);
			Task._Listeners.finally.forEach(
				func => func(context, this)
			);
		} catch (err: unknown) {
			const newResult = {
				taskId: this.id,
				task: this, 
				success: false, 
				error: undefined,
				errorType: undefined, 
			} as Result;
			Task.getTaskError(err, (errErr, type) => {
				newResult.error = errErr;
				newResult.errorType = type;
			});
			let context = this.getChainContext();
			if (context) {
				context = context as Context;
				context.results.push(newResult);
				context.updateTime = Date.now();
			}
		}
	}
	private _chainTasksInitHandler(beforeTask: string[]) {
		if (!this.chainName || this.chainName.trim() === '') {
			this._chainId = Task.getNewId('NoChain');
			const context = {
				results: [], 
				data: this.data,
				chainId: this._chainId, 
				updateTime: Date.now(), 
			} as Context;
			Task._ChainContext.set(this.chainId as string, context);
		}
		for (let taskId of beforeTask) {
			const task = Task.getTask(taskId);
			if (!task) {
				throw new Error('the id in beforeTask arr is invalid')
			}
			if (!this._chainId && task.chainName === this.chainName) {
				this._chainId = task._chainId;
			}
			task._chainTasks.afterTask.push(this.id);
		}
		if (!this._chainId) {
			this._chainId = Task.getNewId(this.chainName as string);
			const context = {
				results: [], 
				data: Task.getChainData(this.chainName), 
				chainName: this.chainName, 
				chainId: this._chainId, 
				updateTime: Date.now(), 
			} as Context;
			Task._ChainContext.set(this.chainId as string, context);
		}
		this._chainTasks.beforeTask = beforeTask;
	}
	
}